<script>
  import $ from '../dfish.js'
  import {
    h
  } from 'vue'
  import WidgetBase from './WidgetBase.vue'
  import ResizeObserver from 'resize-observer-polyfill'

  const onScroll = function(e) {
    if (this.wScrollPercentX) {
      let left = e.target.scrollLeft
      this.scrollXPosition = left == 0 ? 'left' : left >= this.wScrollSizeX ? 'right' : 'center'
      this.$refs.wScrollBarThumbX.style.transform = `translateX(${left/this.wScrollPercentX}px)`
    }
    if (this.wScrollPercentY) {
      let top = e.target.scrollTop
      this.scrollYPosition = top == 0 ? 'top' : top >= this.wScrollSizeY ? 'bottom' : 'middle'
      this.$refs.wScrollBarThumbY.style.transform = `translateY(${top/this.wScrollPercentY}px)`
    }
    this.onScroll(e)
  }

  const onMousedown = function(dnEv) {
    let dir, dnX, dnY, scrLeft, scrTop
    let ov = this.$refs.wScrollOverflow
    let x = this.wScrollPercentX
    let y = this.wScrollPercentY
    $.moveup((mvEv) => {
      if (!dir) {
        dir = this.$refs.wScrollBarThumbX === dnEv.target ? 'X' : 'Y'
        dnX = dnEv.clientX
        dnY = dnEv.clientY
        scrLeft = ov.scrollLeft
        scrTop  = ov.scrollTop
        dnEv.target.parentNode.classList.add('z-drag')
      }
      if (dir === 'X') {
        ov.scrollLeft = scrLeft + ((mvEv.clientX - dnX) * x)
      } else {
        ov.scrollTop = scrTop + ((mvEv.clientY - dnY) * y)
      }
    }, (upEv) => {
      dnEv.target.parentNode.classList.remove('z-drag')
    }, dnEv)
    this.onScrollbarMousedown(dnEv)
  }

  export default {
    mixins: [WidgetBase],
    props: ['scrollX', 'scrollY', 'scrollView'],
    data() {
      return {
        wScrollPercentX: false,
        wScrollPercentY: false,
        wScrollSizeX: false,
        wScrollSizeY: false,
        scrollXPosition: 'left',
        scrollYPosition: 'top',
      }
    },
    watch: {
      scrollXPosition(v)  {
        this.onScrollPosition(v)
      },
      scrollYPosition(v)  {
        this.onScrollPosition(v)
      },
    },
    methods: {
      onScroll(e) {
        //implement
      },
      onScrollbarMousedown(e) {
        //implement
      },
      onScrollPosition() {
        //implement
      },
      // 让元素滚动到可见区域。支持下面两种调用方式 /e -> el|wg, y -> (top,bottom,middle,auto,top+n,bottom+n,n), x -> (left,right,center,auto), p -> ease?, q -> divide(整除数字，让滚动的距离是这个数字的整数倍), r -> callback
      scrollTo(e, y, x, p, q, r) {
        var a = this.ovf();
        if (!a) return;
        var b = this.$refs.wScrollCont || a,
          c = $.bcr(a),
          d = $.bcr(b),
          f = e ? $.bcr(e) : d,
          t, l;
        if (y != null || e) {
          if (y == null || !isNaN(y)) {
            t = $.number(y)
            if (e) t = f.top - d.top - t
          } else if (~y.indexOf('top')) {
            t = f.top - d.top - (+y.slice(3))
          } else if (~y.indexOf('bottom')) {
            t = f.bottom - d.top - c.height + (+y.slice(6))
          } else if (y === 'middle') {
            t = f.top - d.top - ((c.height / 2) - (f.height / 2))
          } else {
            if (f.top < c.top)
              t = a.scrollTop - c.top + f.top
            else if (f.bottom > c.bottom)
              t = a.scrollTop + f.bottom - c.bottom
          }
        }
        if (x != null || e) {
          if (x == null || !isNaN(x)) {
            l = $.number(x)
            if (e) l = f.left - d.left - l
          } else if (~y.indexOf('left')) {
            l = f.left - d.left - (+y.slice(4))
          } else if (~y.indexOf('right')) {
            l = f.right - d.left - c.width + (+y.slice(5))
          } else if (x === 'center') {
            l = f.left - d.left - ((c.width / 2) - (f.width / 2))
          } else {
            if (f.right < c.left || f.left > c.right)
              l = f.left - d.left - ((c.width / 2) - (f.width / 2))
            else if (f.left < c.left)
              l = a.scrollLeft - f.left + c.left
          }
        }
        if (q != null) {
          t != null && (t = t - (t % q))
          l != null && (l = l - (l % q))
        }
        if (p) {
          let g = a.scrollTop
          let h = a.scrollLeft
          $.ease((i) => {
            if (t != null) a.scrollTop = g + (t - g) * i
            if (l != null) a.scrollLeft = h + (l - h) * i
            i === 1 && r && r.call(this)
          }, p)
        } else {
          if (t != null) a.scrollTop = t
          if (l != null) a.scrollLeft = l
          r && r.call(this)
        }
      },
      //@public  /@ e -> el, y -> (top,bottom,middle), p -> ease?, q -> divide, r -> callback
      scrollToY(e, y, p, q, r) {
        if (arguments.length === 0)
          return this.ovf().scrollTop
        if (typeof e !== 'object') {
          r = q, q = p, p = y, y = e, e = null
        }
        if (typeof q === 'function') {
          r = q, q = null
        }
        this.scrollTo(e, y, null, p, q, r)
      },
      //@public  /@ e -> el, x -> (left,right,center), p -> ease?, q -> divide, r -> callback
      scrollToX(e, x, p, q, r) {
        if (arguments.length === 0)
          return this.ovf().scrollLeft
        if (typeof e !== 'object') {
          r = q, q = p, p = x, x = e, e = null
        }
        if (typeof q === 'function') {
          r = q, q = null
        }
        this.scrollTo(e, null, x, p, q, r)
      },
      ovf() {
        return $.br.mobile ? this.el() : this.$refs.wScrollOverflow || this.$refs.wScrollCont
      },
      html_scroll_slots(s) {
        return this.x.scrollX || this.x.scrollY ? [h('div', {
            ref: "wScrollOverflow",
            class: "w-scroll-overflow",
            // style: {
            //   minWidth: $.unitSize(this.x.minWidth),
            //   minHeight: $.unitSize(this.x.minHeight),
            //   maxWidth: $.unitSize(this.x.maxWidth),
            //   maxHeight: $.unitSize(this.x.maxHeight),
            // },
            onscroll: onScroll.bind(this),
            onmouseenter: this.scroll_observe
          }, h('div', {
            class: 'w-scroll-cont',
            style: this.html_scroll_cont_style,
            ref: "wScrollCont"
          }, s || (this.$slots.default && this.$slots.default()))),
          !$.br.mobile && this.x.scrollX && [h('div', {
            class: "w-scrollbar-x",
            ref: "wScrollBarX",
            onmousedown: onMousedown.bind(this)
          }, h('div', {
            class: "w-scrollbar-thumb",
            ref: "wScrollBarThumbX"
          }))],
          !$.br.mobile && this.x.scrollY && [h('div', {
            class: "w-scrollbar-y",
            ref: "wScrollBarY",
            onmousedown: onMousedown.bind(this)
          }, h('div', {
            class: "w-scrollbar-thumb",
            ref: "wScrollBarThumbY"
          }))]] : (s || (this.$slots.default && this.$slots.default()))
        
      },
      scroll_observe() {
        if ($.br.mobile || !this.$refs.wScrollOverflow || !this.el()) return
        const wrap = this.el().getBoundingClientRect()
        const flow = this.$refs.wScrollOverflow.getBoundingClientRect()
        const cont = this.$refs.wScrollCont.getBoundingClientRect()
        let xSize = cont.width > flow.width && cont.width - flow.width
        let ySize = cont.height > flow.height && cont.height - flow.height
        let xPercent = flow.width / cont.width
        let yPercent = flow.height / cont.height
        if (xSize && xSize <= 1) xSize = false //@fixme 横向滚动时，width:fit-content的设置会让有小数点的宽度变成整数。目前先加一个像素容错
        if (this.x.scrollX) {
          let thbX = xPercent * flow.width
          this.$refs.wScrollBarThumbX.style.width = Math.max(20, thbX) + 'px'
          this.wScrollSizeX = xSize
          this.wScrollPercentX = xSize && (xSize / ((1 - xPercent) * (wrap.width - (thbX < 20 ? 20 - thbX : 0))))
        } 
        if (this.x.scrollY) {
          let thbY = yPercent * flow.height
          this.$refs.wScrollBarThumbY.style.height = Math.max(20, thbY) + 'px'
          this.wScrollSizeY = ySize
          this.wScrollPercentY = ySize && (ySize / ((1 - yPercent) * (wrap.height - (thbY < 20 ? 20 - thbY : 0))))
        }
      }
    },
    computed: {
      html_scroll_cont_style() {
        // implement
      },
      html_scroll_class() {
        let sx = this.x.scrollX
        let sy = this.x.scrollY
        return {
          'w-scroll': sx || sy,
          'w-scroll-x': sx,
          'w-scroll-y': sy,
          'z-scroll-x-show': this.wScrollPercentX,
          'z-scroll-y-show': this.wScrollPercentY,
          // 'z-scroll-x-left': sx && this.scrollXPosition == 'left',
          // 'z-scroll-x-center': sx && this.scrollXPosition == 'center',
          // 'z-scroll-x-right': sx && this.scrollXPosition == 'right',
          // 'z-scroll-y-top': sy && this.scrollYPosition == 'top',
          // 'z-scroll-y-middle': sy && this.scrollYPosition == 'middle',
          // 'z-scroll-y-bottom': sy && this.scrollYPosition == 'bottom',
        }
      },

    },
    mounted() {
      if (!this.isEntity) return
      if (this.x.scrollView && this.el()) {
        setTimeout(() => {
          let el = this.el().querySelector(this.x.scrollView)
          el && this.scrollToY(el, 'middle')
        })
      }
      //在PC端生成滚动条。移动端不生成
      if ($.br.mobile || !(this.x.scrollX || this.x.scrollY)) return
      this.$refs.wScrollOverflow && new ResizeObserver(this.scroll_observe).observe(this.$refs.wScrollOverflow)
      this.$refs.wScrollCont && new ResizeObserver(this.scroll_observe).observe(this.$refs.wScrollCont)
    }
  }
</script>

<style>
  .w-scroll {
    position: relative;
    display: flex;
  }

  .w-scroll-overflow {
    flex: 1;
    box-sizing: border-box;
    scrollbar-width: none;
  }

  .w-scroll-x>.w-scroll-overflow {
    overflow-x: auto;
  }

  .w-scroll-y>.w-scroll-overflow {
    overflow-y: auto;
  }

  .w-scroll-cont {
    min-width: 100%;
    box-sizing: border-box;
  }

  .w-scroll-x>.w-scroll-overflow>.w-scroll-cont {
    width: fit-content;
  }

  .w-scroll-overflow::-webkit-scrollbar {
    width: 0;
    height: 0;
  }

  .w-scrollbar-thumb {
    background-color: rgba(200, 200, 200, .9);
  }

  .w-scrollbar-thumb:hover {
    background-color: rgba(180, 180, 180, 1);
  }

  .w-scroll-x:hover>.w-scrollbar-x,
  .w-scroll-x.z-hv>.w-scrollbar-x,
  .w-scroll-y:hover>.w-scrollbar-y,
  .w-scroll-y.z-hv>.w-scrollbar-y,
  .w-scrollbar-x.z-drag,
  .w-scrollbar-y.z-drag {
    visibility: visible;
  }
  
  .w-scroll.z-scroll-x-show > .w-scrollbar-x,
  .w-scroll.z-scroll-y-show > .w-scrollbar-y {
    display: block;
  }

  .w-scrollbar-x {
    display: none;
    visibility: hidden;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 12px;
    z-index: 10;
    user-select: none;
  }

  .w-scrollbar-y {
    display: none;
    visibility: hidden;
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 12px;
    z-index: 10;
    user-select: none;
  }
  
  .w-scrollbar-thumb {
    position: absolute;
    transition-property: top,left;
    transition-duration: .1s;
  }

  .w-scrollbar-x > .w-scrollbar-thumb {
    top: 4px;
    bottom: 0;
    min-width: 20px;
  }


  .w-scrollbar-y > .w-scrollbar-thumb {
    left: 4px;
    right: 0;
    min-height: 20px;
  }
  
  .w-scrollbar-x:hover > .w-scrollbar-thumb {
    top: 0;
  }

  .w-scrollbar-y:hover > .w-scrollbar-thumb {
    left: 0;
  }
</style>
